iT邦幫忙

2024 iThome 鐵人賽

DAY 5
0

目前在 Next App 中點擊登出後,在嘗試重新登入的時候會發現不會再次跳出先前的 Keycloak 登入畫面,這是因為當 Next 這邊登出時只是清理掉 Next Auth 自己的 Session 紀錄,並沒有將 Keycloak 的 Session 登出,而當重新再登入時因為 Keycloak 還保持著登入狀態所以就跳過輸入視窗直接登入了。

這種作法是刻意的,就像在用了 Facebook 登入某些應用後,你不會想要再登出那隻應用時把 Facebook 也一併登出了。

但因為這次預計是以 Keycloak 中控每個跟主應用銜接的服務的登入狀態,會希望在主應用登出時可以把所有服務都一併登出,所以需要做成同步登出的功能。

這邊需要另外呼叫 Keycloak 的登出端口來實現同步登出,首先做一個處理這件事的 api。

// src/server/api/routers/auth.ts
import { env } from "~/env";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";

export const authRouter = createTRPCRouter({
  federatedLogout: protectedProcedure.query(async ({ ctx }) => {
    const userId = ctx.session.user.id;
    if (!userId) {
      throw new Error("No user id found");
    }

    const account = await ctx.db.account.findFirst({
      where: {
        userId,
        provider: "keycloak",
      },
    });

    if (!account) {
      throw new Error("No account found");
    }

    if (!account.id_token) {
      throw new Error("No id token found");
    }

    const logOutUrl = new URL(
      `${process.env.KEYCLOAK_ISSUER}/protocol/openid-connect/logout`,
    );
    logOutUrl.searchParams.set("id_token_hint", account.id_token);
    logOutUrl.searchParams.set("post_logout_redirect_uri", env.CLIENT_BASE_URL);

    return logOutUrl.toString();
  }),
});

在 trpc 中建一個 auth.federatedLogout 的 api ,主要功能:

  • 在使用者點登出前先呼叫這隻 api ,以取得登出 keycloak 用的 url。
  • 要取得 url ,需要知道 user 當初登入的 id_token 跟 keycloak 的站點位置,這些可以從 account 中取得。
    在 NextAuth 中 user 是可能會有好幾個 account 的,但這次的架構只使用唯一的 keycloak 作為登入功能提供者,所以不考慮有多個 account 的狀況。
  • 將資訊組合成 url 後回傳。

接著前端這邊新建一個負責登出的按鈕元件。

"use client";
import { signOut } from "next-auth/react";
import { api } from "~/trpc/react";

const ActionSignOut = () => {
  const { data } = api.auth.federatedLogout.useQuery();
  const onClick = async () => {
    if (data) {
      await signOut({ redirect: false });
      window.location.href = data;
    }
  };

  return (
    <>
      <button
        className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20"
        onClick={onClick}
      >
        {"Sign Out"}
      </button>
    </>
  );
};

export default ActionSignOut;

首先從上面的 api 中取得登出 keycloak 的 url,確認成功後再登出 NextAuth 清空這一端的 session ,再來導向登出 keycloak 的 url ,實現雙邊同步登出。

不過到這邊發現有個問題,在登出又重新登入後,account 中的 id_token 貌似沒更新,導致在嘗試第二次登出時會出現一個確認登出的畫面,因為 id_token 對不上。

看來還需要另外想辦法處理這個 token rotation 的問題,查資料的時候好像也有人說到 refresh token 的處理會很麻煩,得再研究研究。


上一篇
Keycloak 與 NextJs 登入
下一篇
自訂登入頁面樣式,使用 Keycloakify
系列文
Awesome self hosted 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言